// rgTransformation.h

#ifndef _RG_TRANSFORMATION_H_
#define _RG_TRANSFORMATION_H_

#include <graphics/rgMatrix4.h>
#include <graphics/rgRay.h>
class KERNELDLL rgTransformation
{
public:
	rgMatrix4 m_matrix;
	//rgMatrix4 m_inverse;
		
	/** Constructor por defecto
	* 
	*/
	rgTransformation()
	{
		m_matrix.setIdentity();
		//m_inverse.setIdentity();
	}

	/**
	*/
	rgTransformation( const rgMatrix4& m )
	{
		m_matrix = m;
		//m_inverse = m.invert();
	}

	/**
	*/
	/*
	rgTransformation( const rgMatrix4& m, const rgMatrix4& mi )
	{
		m_matrix = m;
		m_inverse = mi;
	}
	*/

	/**
	*/
	inline rgTransformation& setTraslate( const rgReal& x, const rgReal& y, const rgReal& z )
	{

/*		m_matrix.setIdentity();
		m_inverse.setIdentity();

		m_matrix.m_r0[3] = x;
		m_matrix.m_r1[3] = y;
		m_matrix.m_r2[3] = z;

		m_inverse.m_r0[3] = -x;
		m_inverse.m_r1[3] = -y;
		m_inverse.m_r2[3] = -z;
*/
		m_matrix.set(1, 0, 0, x, 
					 0, 1, 0, y, 
					 0, 0, 1, z, 
					 0, 0, 0, 1);

		/*m_inverse.set(1, 0, 0, -x, 
					  0, 1, 0, -y, 
					  0, 0, 1, -z, 
					  0, 0, 0,  1);*/

		return *this;
	}

	/**
	*/
	inline rgTransformation& traslate( const rgReal& x, const rgReal& y, const rgReal& z )
	{
		rgMatrix4 m(1, 0, 0, x, 
					0, 1, 0, y, 
					0, 0, 1, z, 
					0, 0, 0, 1);

		m >> m_matrix;
/*		
		m.m_m[3]  = -x;
		m.m_m[7]  = -y;
		m.m_m[11] = -z;
		
		m >> m_inverse;
*/
		return *this;
	}

	/**
	*/
	inline rgTransformation& setTraslate( const rgVector& v )
	{
		m_matrix.set(1, 0, 0, v.x, 
					 0, 1, 0, v.y, 
					 0, 0, 1, v.z, 
					 0, 0, 0, 1);

/*		m_inverse.set(1, 0, 0, -v.x, 
					  0, 1, 0, -v.y, 
					  0, 0, 1, -v.z, 
					  0, 0, 0,  1);*/

		return *this;
	}

	/**
	*/
	inline rgTransformation& traslate( const rgVector& v )
	{
		rgMatrix4 m(1, 0, 0, v.x, 
					0, 1, 0, v.y, 
					0, 0, 1, v.z, 
					0, 0, 0,  1  );

		m >> m_matrix;
		
/*		m.m_m[3]  = -v.x;
		m.m_m[7]  = -v.y;
		m.m_m[11] = -v.z;
		
		m >> m_inverse;*/

		return *this;
	}

	/**
	*/
	inline rgTransformation& setScale( const rgReal& x, const rgReal& y, const rgReal& z )
	{
		m_matrix.set(x, 0, 0, 0, 
					 0, y, 0, 0, 
					 0, 0, z, 0, 
					 0, 0, 0, 1);

/*		m_inverse.set(1/x, 0,  0,  0, 
					   0, 1/y, 0,  0, 
					   0,  0, 1/z, 0, 
					   0,  0,  0,  1);*/

		return *this;
	}
	
	/**
	*/
	inline rgTransformation& scale( const rgReal& x, const rgReal& y, const rgReal& z )
	{
		rgMatrix4 m(x, 0, 0, 0, 
					0, y, 0, 0, 
					0, 0, z, 0, 
					0, 0, 0, 1);

		m >> m_matrix;
		
/*		m.m_m[0]  = 1/x;
		m.m_m[5]  = 1/y;
		m.m_m[10] = 1/z;
		
		m >> m_inverse;*/

		return *this;
	}
	
	/**
	*/
	inline rgTransformation& setRotateX( const rgReal& angle )
	{
		rgReal c = rgCos( angle );
		rgReal s = rgSin( angle );

		m_matrix.set(1, 0, 0, 0, 
					 0, c,-s, 0, 
					 0, s, c, 0, 
					 0, 0, 0, 1);

/*		m_inverse.set(1, 0, 0, 0, 
					  0, c, s, 0, 
					  0,-s, c, 0, 
					  0, 0, 0, 1);*/

		return *this;
	}

	/**
	*/
	inline rgTransformation& rotateX( const rgReal& angle )
	{
		rgReal c = rgCos( angle );
		rgReal s = rgSin( angle );

		rgMatrix4 m( 1, 0, 0, 0, 
					 0, c,-s, 0, 
					 0, s, c, 0, 
					 0, 0, 0, 1);

		m >> m_matrix;

/*		m.set(1, 0, 0, 0, 
			  0, c, s, 0, 
			  0,-s, c, 0, 
			  0, 0, 0, 1);
		
		m >> m_inverse;*/

		return *this;
	}

	/**
	*/
	inline rgTransformation& setRotateY( const rgReal& angle )
	{
		rgReal c = rgCos( angle );
		rgReal s = rgSin( angle );

		m_matrix.set(c, 0, s, 0, 
					 0, 1, 0, 0, 
					-s, 0, c, 0, 
					 0, 0, 0, 1);

/*		m_inverse.set(c, 0, -s, 0, 
					  0, 1, 0, 0, 
					  s, 0, c, 0, 
					  0, 0, 0, 1);*/
		return *this;
	}

		/**
	*/
	inline rgTransformation& rotateY( const rgReal& angle )
	{
		rgReal c = rgCos( angle );
		rgReal s = rgSin( angle );

		rgMatrix4 m( c, 0, s, 0, 
					 0, 1, 0, 0, 
					-s, 0, c, 0, 
					 0, 0, 0, 1);

		m >> m_matrix;

/*		m.set(c, 0, -s, 0, 
			  0, 1, 0, 0, 
			  s, 0, c, 0, 
			  0, 0, 0, 1);
		
		m >> m_inverse;*/

		return *this;
	}

	/**
	*/
	inline rgTransformation& setRotateZ( const rgReal& angle )
	{
		rgReal c = rgCos( angle );
		rgReal s = rgSin( angle );

		m_matrix.set(c,-s, 0, 0, 
					 s, c, 0, 0, 
					 0, 0, 1, 0, 
					 0, 0, 0, 1);

/*		m_inverse.set(c, s, 0, 0, 
					 -s, c, 0, 0, 
					  0, 0, 1, 0, 
					  0, 0, 0, 1);*/

		return *this;
	}
	/**
	*/
	inline rgTransformation& rotateZ( const rgReal& angle )
	{
		rgReal c = rgCos( angle );
		rgReal s = rgSin( angle );

		rgMatrix4 m( c,-s, 0, 0, 
					 s, c, 0, 0, 
					 0, 0, 1, 0, 
					 0, 0, 0, 1);

		m >> m_matrix;

/*		m.set(c, s, 0, 0, 
			 -s, c, 0, 0, 
			  0, 0, 1, 0, 
			  0, 0, 0, 1);

		m >> m_inverse;*/

		return *this;
	}

	/**
	*/
	inline rgTransformation& rotate( const rgReal& angle, const rgVector& axis)
	{
		//m_matrix.setIdentity();
		//m_inverse.setIdentity();


		rgVector a = axis;
		a.normalizeMe();
		rgReal s = rgSin(angle);
		rgReal c = rgCos(angle);
		rgMatrix4 m;
		m.m_a00 = a.x * a.x + (1.0 - a.x * a.x) * c;
		m.m_a01 = a.x * a.y * (1.0 - c) - a.z * s;
		m.m_a02 = a.x * a.z * (1.0 - c) + a.y * s;
		m.m_a03 = 0;

		m.m_a10 = a.x * a.y * (1.0 - c) + a.z * s;
		m.m_a11 = a.y * a.y + (1.0 - a.y * a.y) * c;
		m.m_a12 = a.y * a.z * (1.0 - c) - a.x * s;
		m.m_a13 = 0;
	
		m.m_a20 = a.x * a.z * (1.0 - c) - a.y * s;
		m.m_a21 = a.y * a.z * (1.0 - c) + a.x * s;
		m.m_a22 = a.z * a.z + (1.0 - a.z * a.z) * c;
		m.m_a23 = 0;

		m.m_a30 = 0;
		m.m_a31 = 0;
		m.m_a32 = 0;
		m.m_a33 = 1;

		//m_inverse = m_matrix.transpose();
		m >> m_matrix;
		return *this;
	}

	/**
	*/
	inline rgPoint operator() ( const rgPoint& p ) const
	{
		rgPoint pOut( m_matrix.m_a00 * p.x + m_matrix.m_a01 * p.y + m_matrix.m_a02 * p.z + m_matrix.m_a03,
					  m_matrix.m_a10 * p.x + m_matrix.m_a11 * p.y + m_matrix.m_a12 * p.z + m_matrix.m_a13,
					  m_matrix.m_a20 * p.x + m_matrix.m_a21 * p.y + m_matrix.m_a22 * p.z + m_matrix.m_a23);
		
		rgReal w = m_matrix.m_a30 * p.x + m_matrix.m_a31 * p.y + m_matrix.m_a32 * p.z + m_matrix.m_a33;
		
		ASSERT(w != 0);
		
		if(pOut.w == 1.0) return pOut;

		return pOut/w;
	}

	/**
	*/
	inline void operator() ( rgPoint& p ) const
	{
		p.set( m_matrix.m_a00 * p.x + m_matrix.m_a01 * p.y + m_matrix.m_a02 * p.z + m_matrix.m_a03,
			   m_matrix.m_a10 * p.x + m_matrix.m_a11 * p.y + m_matrix.m_a12 * p.z + m_matrix.m_a13,
			   m_matrix.m_a20 * p.x + m_matrix.m_a21 * p.y + m_matrix.m_a22 * p.z + m_matrix.m_a23);
		
		rgReal w = m_matrix.m_a30 * p.x + m_matrix.m_a31 * p.y + m_matrix.m_a32 * p.z + m_matrix.m_a33;
		
		ASSERT(w != 0);
		
		if(p.w != 1.0) p = p / w;
	}

	/**
	*/
	inline rgVector operator() ( const rgVector& v ) const
	{
		return rgVector( m_matrix.m_a00 * v.x + m_matrix.m_a01 * v.y + m_matrix.m_a02 * v.z,
						 m_matrix.m_a10 * v.x + m_matrix.m_a11 * v.y + m_matrix.m_a12 * v.z,
						 m_matrix.m_a20 * v.x + m_matrix.m_a21 * v.y + m_matrix.m_a22 * v.z);
	}
	
	/**
	*/
	inline void operator() ( rgVector& v ) const
	{
		v.set( m_matrix.m_a00 * v.x + m_matrix.m_a01 * v.y + m_matrix.m_a02 * v.z,
			   m_matrix.m_a10 * v.x + m_matrix.m_a11 * v.y + m_matrix.m_a12 * v.z,
			   m_matrix.m_a20 * v.x + m_matrix.m_a21 * v.y + m_matrix.m_a22 * v.z);
	}

	/**
	*/
	inline rgRay operator() ( const rgRay& r ) const
	{
		rgRay ray;
		ray.setDirection( (*this)(r.m_direction) );
		ray.setOrigin( (*this)(r.m_origin) );

		ray.maxL = r.maxL;
		return ray;
	}

	/**
	*/
	inline void operator() ( rgRay& r ) const
	{
		(*this)(r.m_direction);
		(*this)(r.m_origin);
	}
	/**
	*/
	inline rgTransformation operator*( const rgTransformation& t2 ) const
	{
		//rgTransformation t;
		//t.m_matrix = m_matrix * t2.m_matrix;
		//t.m_inverse = t2.m_inverse * m_inverse;

		return rgTransformation(m_matrix * t2.m_matrix);//, t2.m_inverse * m_inverse);
	}

	/**
	*/
	inline rgTransformation& operator*=( const rgTransformation& t2 )
	{
		m_matrix = m_matrix * t2.m_matrix;
		//m_inverse = t2.m_inverse * m_inverse;

		return *this;
	}

	/**
	*/
	inline void changeSystem()
	{
		m_matrix.changeSystem();
		//m_inverse.changeSystem();
	}
};

#endif // _RG_TRANSFORMATION_H_
